#include <retro.h>
#include "retro.creator.h"

#define COLOR_POINT_COUNT   360
#define COLOR_PICKER_RADIUS 10
#define COLOR_HUE_INCREMENT 10
#define COLOR_SAT_INCREMENT 10

using namespace Retro::Creator;
using namespace Retro::Creator::GUI;

ColorWheel::ColorWheel()
{
  mColorWheel   = NULL;
  mWheelRadius  = 0;
  mState        = StateNormal;

  setColor(0xFFFFFFFF);
}

ColorWheel::~ColorWheel()
{
  SafeDelete(mColorWheel);
}

VOID  ColorWheel::getWheelBounds(Gdip::Rect& oBounds)
{
  Geom::Size wSize = getSize();

  oBounds = Gdip::Rect(
    (wSize.cx - mWheelRadius * 2) / 2,
    (wSize.cy - mWheelRadius * 2) / 2,
    mWheelRadius * 2,
    mWheelRadius * 2);
}

VOID  ColorWheel::getWheelCursorBounds(Gdip::Rect& oBounds)
{
  Geom::Size wSize = getSize();

  oBounds = Gdip::Rect(
    mCursorPos.x - COLOR_PICKER_RADIUS,
    mCursorPos.y - COLOR_PICKER_RADIUS,
    COLOR_PICKER_RADIUS * 2,
    COLOR_PICKER_RADIUS * 2);
}

VOID  ColorWheel::setWheelRadius(INT iValue)
{
  Gdip::Color wColors[COLOR_POINT_COUNT];
  Gdip::Point wPoints[COLOR_POINT_COUNT];
  Geom::Point wOrigin = { iValue, iValue };
  Geom::Point wAxis   = { iValue * 2 + 2, iValue };

  for (INT i = 0; i < COLOR_POINT_COUNT; ++i)
  {
    Geom::Point wPoint = wAxis.rotate(wOrigin, i * PI * 2 / COLOR_POINT_COUNT);
    Geom::HSVColor wColor = { 255, i * 360 / COLOR_POINT_COUNT, 255, 255 };

    wPoints[i] = Gdip::Point(wPoint.x, wPoint.y);
    wColors[i] = (COLOR)wColor;
  }

  SafeDelete(mColorWheel);

  mWheelRadius  = iValue;
  mColorWheel   = new Gdip::Bitmap(iValue * 2, iValue * 2);

  Gdip::PathGradientBrush wBrush(wPoints, COLOR_POINT_COUNT);

  INT wColorCount = COLOR_POINT_COUNT;

  wBrush.SetSurroundColors(wColors, &wColorCount);
  wBrush.SetCenterColor(0xFFFFFFFF);
  wBrush.SetCenterPoint(Gdip::Point(wOrigin.x, wOrigin.y));

  Gdip::Graphics g(mColorWheel);

  g.SetSmoothingMode(Gdip::SmoothingModeAntiAlias);
  g.FillEllipse(&wBrush, 0, 0, iValue * 2 - 1, iValue * 2 - 1);
}

VOID  ColorWheel::onControlPaint(ControlPaintEvent& iEvent)
{
  Gdip::Rect wWheel;
  Gdip::Rect wCursorWheel;
  Gdip::Pen wBorderPen(0x7F000000);

  getWheelBounds(wWheel);
  getWheelCursorBounds(wCursorWheel);

  if (mColorWheel)
  {
    iEvent.graphics.DrawCachedBitmap(
      &Gdip::CachedBitmap(mColorWheel, &iEvent.graphics),
      wWheel.X, wWheel.Y);
  }

  wWheel.Width--;
  wWheel.Height--;

  wWheel.Inflate(-1, -1);
  iEvent.graphics.DrawEllipse(&wBorderPen, wWheel);
  wWheel.Inflate(-1, -1);
  iEvent.graphics.DrawEllipse(&Gdip::Pen(0x7FFFFFFF), wWheel);

  if (!isFocused())
  {
    wCursorWheel.Inflate(
      -wCursorWheel.Width / 4,
      -wCursorWheel.Height / 4);
  }

  iEvent.graphics.FillEllipse(
    &Gdip::SolidBrush(mRGBColor.value),
    wCursorWheel);

  wCursorWheel.Inflate(-1, -1);
  iEvent.graphics.DrawEllipse(&wBorderPen, wCursorWheel);
  wCursorWheel.Inflate(-1, -1);
  iEvent.graphics.DrawEllipse(&Gdip::Pen(0x7FFFFFFF), wCursorWheel);
}

VOID  ColorWheel::onMouseDown(Event::MouseEvent& iEvent)
{
  if (hitTestWheel(iEvent.point))
  {
    mState = StatePressed;
    focus();
  }
}

VOID  ColorWheel::onMouseUp(Event::MouseEvent& iEvent)
{
  mState = StateNormal;
}

VOID  ColorWheel::onMouseMove(Event::MouseEvent& iEvent)
{
  hitTestWheel(iEvent.point);
}

VOID  ColorWheel::onMouseWheel(Event::MouseWheelEvent& iEvent)
{
  INT wValue = (INT)mHSVColor.hue +
    iEvent.wheelDelta * COLOR_HUE_INCREMENT / WHEEL_DELTA;

  if (wValue < 0) wValue += 360;
  if (wValue >= 360) wValue -= 360;

  mHSVColor.hue = (UINT)wValue;
  setColor(mHSVColor);
}

VOID  ColorWheel::onKeyDown(Event::KeyEvent& iEvent)
{
  INT wValue;

  switch (iEvent.vkCode)
  {
  case VK_LEFT:
    wValue = (INT)mHSVColor.hue - COLOR_HUE_INCREMENT;
    if (wValue < 0) wValue += 360;

    mHSVColor.hue = (UINT)wValue;
    setColor(mHSVColor);
    break;
  case VK_RIGHT:
    wValue = (INT)mHSVColor.hue + COLOR_HUE_INCREMENT;
    if (wValue >= 360) wValue -= 360;

    mHSVColor.hue = (UINT)wValue;
    setColor(mHSVColor);
    break;
  case VK_UP:
    mHSVColor.sat = (UINT)min(255, (INT)mHSVColor.sat + COLOR_SAT_INCREMENT);

    setColor(mHSVColor);
    break;
  case VK_DOWN:
    mHSVColor.sat = (UINT)max(0, (INT)mHSVColor.sat - COLOR_SAT_INCREMENT);

    setColor(mHSVColor);
    break;
  }
}

VOID  ColorWheel::onFocus(Event::FocusEvent& iEvent)
{
  invalidate();
}

VOID  ColorWheel::onSize(Event::SizeEvent& iEvent)
{
  Control::onSize(iEvent);
  setColor(mRGBColor);
}

BOOL  ColorWheel::hitTestWheel(const Geom::Point& iPoint)
{
  Geom::Point wOrigin = getSize() / 2L;
  Geom::Point wAxis   = wOrigin;

  wAxis.x += mWheelRadius;

  if (!(mState == StatePressed ||
    (mState == StateNormal && GetKeyState(VK_LBUTTON) >> 1 &&
    wOrigin.distanceSq(iPoint) < mWheelRadius * mWheelRadius)))
  {
    return FALSE;
  }

  DOUBLE wHue = DEGREE(iPoint.angle(wOrigin, wAxis));
  DOUBLE wSat = iPoint.distance(wOrigin);

  while (wHue >= 360) wHue -= 360;
  while (wHue < 0)    wHue += 360;

  wSat = min(mWheelRadius, wSat);
  wSat = wSat * 255 / mWheelRadius;

  Geom::HSVColor wHSV = mHSVColor;

  wHSV.hue = (UINT)wHue;
  wHSV.sat = (UINT)wSat;

  setColor(wHSV);

  return TRUE;
}

VOID  ColorWheel::setColor(COLOR iValue)
{
  mHSVColor = Geom::RGBColor::get(iValue).toHSV();

  Gdip::Rect  wInvalid;
  DOUBLE      wAngle    = RADIAN(mHSVColor.hue);
  Geom::Point wOrigin   = getSize() / 2L;
  Geom::Point wCursor;

  wCursor.x = wOrigin.x + mHSVColor.sat * mWheelRadius / 255;
  wCursor.y = wOrigin.y;

  getWheelCursorBounds(wInvalid);
  wInvalid.Inflate(COLOR_PICKER_RADIUS, COLOR_PICKER_RADIUS);
  invalidate(wInvalid);

  mCursorPos  = wCursor.rotate(wOrigin, wAngle);
  mRGBColor   = Geom::RGBColor::get(iValue);

  getWheelCursorBounds(wInvalid);
  wInvalid.Inflate(COLOR_PICKER_RADIUS, COLOR_PICKER_RADIUS);
  invalidate(wInvalid);

  sendNotify(ColorWheelNotifySelection);
}
